﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace CoolCode.Dynamic
{
	public static class InstanceCreatorExtensions
	{
		/// <summary>
		/// 根据类型创建实例
		/// </summary>
		/// <param name="type"></param>
		/// <returns></returns>
		public static object CreateInstance(this Type type)
		{
			return ExpressionActivator.CreateInstance(type);
		}

		/// <summary>
		/// 根据类型和城市创建实例
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="type"></param>
		/// <param name="arg"></param>
		/// <returns></returns>
		public static object CreateInstance<T>(this Type type, T arg)
		{
			var creator = Singleton<InstanceCreator<T>>.GetInstance();
			return creator.CreateInstance(type, arg);
		}

		/// <summary>
		/// 根据类型和城市创建实例
		/// </summary>
		/// <typeparam name="T1"></typeparam>
		/// <typeparam name="T2"></typeparam>
		/// <param name="type"></param>
		/// <param name="arg1"></param>
		/// <param name="arg2"></param>
		/// <returns></returns>
		public static object CreateInstance<T1, T2>(this Type type, T1 arg1, T2 arg2)
		{
			var creator = Singleton<InstanceCreator<T1, T2>>.GetInstance();
			return creator.CreateInstance(type, arg1, arg2);
		}

		#region InstanceCreators

		/// <summary>
		/// 实例创建者（创建构造函数含1参数的对象）
		/// </summary>
		/// <typeparam name="T">参数</typeparam>
		public class InstanceCreator<T>
		{
			private static readonly Dictionary<Type, Func<T, object>> cache =
				new Dictionary<Type, Func<T, object>>();

			public object CreateInstance(Type type, T arg)
			{
				Func<T, object> createInstanceDelegate = null;

				if (!cache.TryGetValue(type, out createInstanceDelegate))
				{
					lock (this)
					{
						if (!cache.TryGetValue(type, out createInstanceDelegate))
						{
							createInstanceDelegate = CreateInstanceDelegate(type);
							cache.Add(type, createInstanceDelegate);
						}
					}
				}

				return createInstanceDelegate(arg);
			}

			public static Func<T, object> CreateInstanceDelegate(Type type)
			{
				Type paramType = typeof(T);
				var constructor = type.GetConstructor(new Type[] { paramType });
				var param = new ParameterExpression[] { Expression.Parameter(paramType, "arg") };

				NewExpression newExp = Expression.New(constructor, param);
				Expression<Func<T, object>> lambdaExp =
					Expression.Lambda<Func<T, object>>(newExp, param);
				Func<T, object> func = lambdaExp.Compile();
				return func;
			}
		}

		/// <summary>
		/// 实例创建者（创建构造函数含2参数的对象）
		/// </summary>
		/// <typeparam name="T1">参数1</typeparam>
		/// <typeparam name="T2">参数2</typeparam>
		public class InstanceCreator<T1, T2>
		{
			private static readonly Dictionary<Type, Func<T1, T2, object>> cache =
				new Dictionary<Type, Func<T1, T2, object>>();

			public object CreateInstance(Type type, T1 arg1, T2 arg2)
			{
				Func<T1, T2, object> createInstanceDelegate = null;
				if (!cache.TryGetValue(type, out createInstanceDelegate))
				{
					lock (this)
					{
						if (!cache.TryGetValue(type, out createInstanceDelegate))
						{
							createInstanceDelegate = CreateInstanceDelegate(type);
							cache.Add(type, createInstanceDelegate);
						}
					}
				}
				return createInstanceDelegate(arg1, arg2);
			}

			public static Func<T1, T2, object> CreateInstanceDelegate(Type type)
			{
				Type paramType1 = typeof(T1);
				Type paramType2 = typeof(T2);
				var constructor = type.GetConstructor(new Type[] { paramType1, paramType2 });
				var param = new ParameterExpression[] { 
					Expression.Parameter(paramType1, "arg1"),
					Expression.Parameter(paramType2, "arg2")};

				NewExpression newExp = Expression.New(constructor, param);
				Expression<Func<T1, T2, object>> lambdaExp =
					Expression.Lambda<Func<T1, T2, object>>(newExp, param);
				Func<T1, T2, object> func = lambdaExp.Compile();
				return func;
			}
		}

		#endregion
	}
}
